﻿// This file is part of Code4Public (http://code4public.codeplex.com)
// Copyright 2011 Sina Iravanian - <sina@sinairv.com>
//
// This source file(s) may be redistributed, altered and customized
// by any means PROVIDING the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
// ------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Code4Public.Settings;
using System.Diagnostics;

namespace Code4Public.GUI
{
    public partial class ThemePreviewBox : UserControl
    {
        public ThemePreviewBox()
        {
            InitializeComponent();

            this.Theme = new Theme(CodeConstants.GUI.ThemePreviewDefaultUntitledName);
            this.InternalPadding = CodeConstants.GUI.ThemePreviewDefaultInternalPadding;
        }

        public int InternalPadding { get; set; }
        public Theme Theme { get; set; }

        private void DrawBorder(Graphics g, BorderInfo border, int x1, int y1, int x2, int y2)
        {
            Debug.Assert(border != null);
            Pen pen = new Pen(border.Color, 1.0f);
            if (border.Top)
            {
                g.DrawLine(pen, new Point(x1, y1), new Point(x2, y1));
            }

            if (border.Left)
            {
                g.DrawLine(pen, new Point(x1, y1), new Point(x1, y2));
            }

            if (border.Right)
            {
                g.DrawLine(pen, new Point(x2, y1), new Point(x2, y2));
            }

            if (border.Bottom)
            {
                g.DrawLine(pen, new Point(x1, y2), new Point(x2, y2));
            }
        }

        private void FillBorder(Graphics g, Color color, int x1, int y1, int x2, int y2)
        {
            g.FillRectangle(new SolidBrush(color), x1, y1 , x2 - x1 + 1, y2 - y1 + 1);
        }

        private int DrawLineNumbers(Graphics g, int x1, int y1, int x2, int y2)
        {
            FontStyle lineNoFontStyle = FontStyle.Regular;
            if (Theme.LineNumbersFont.Bold)
                lineNoFontStyle = FontStyle.Bold;
            if (Theme.LineNumbersFont.Italic)
                lineNoFontStyle = lineNoFontStyle | FontStyle.Italic;

            Font lineNoFont = new Font(CodeConstants.GUI.ThemePreviewFontFace, Theme.MainContentFontSize, lineNoFontStyle, GraphicsUnit.Point);

            int maxDigitCount = 3;
            string sampleLineNo = "100";
            if (Theme.LineNumbersShowStringAfter)
            {
                sampleLineNo += Theme.LineNumbersStringAfter;
            }

            for (int i = 0; i < Theme.LineNumbersSpacesBefore; ++i)
            {
                sampleLineNo = " " + sampleLineNo;
            }

            for (int i = 0; i < Theme.LineNumbersSpacesAfter; ++i)
            {
                sampleLineNo += " ";
            }


            SizeF lineSize = MeasureStringWithSpaces(g, sampleLineNo, lineNoFont);
            int lineHeight = (int)lineSize.Height;
            int lineWidth = (int)lineSize.Width;

            int newX2 = x1 + lineWidth + 2;

            FillBorder(g, Theme.LineNumbersBackColor, x1, y1, newX2, y2);

            if (Theme.LineNumbersShowSeperatorLine)
            {
                g.DrawLine(new Pen(Theme.LineNumbersSeperatorLineColor, 1.0f), newX2, y1, newX2, y2 - 1); 
            }

            int dotLineCount = CodeConstants.GUI.ThemePreviewDotLinesCount;
            int lineCount = (y2 - y1) / lineHeight;
            int linesAfter = (lineCount - dotLineCount) / 2;
            int linesBefore = lineCount - linesAfter - dotLineCount;

            int li = 0;
            for (li = 0; li < linesBefore; ++li)
            {
                g.DrawString(GetLineNumberString(li+1, maxDigitCount), lineNoFont, new SolidBrush(Theme.LineNumbersFont.Color), x1+1, y1 + li * lineHeight);
            }

            for (; li < linesBefore + dotLineCount; ++li)
            {
                g.DrawString(".", lineNoFont, new SolidBrush(Theme.LineNumbersFont.Color), (x1 + 1 + newX2)/2, y1 + li * lineHeight);
            }

            for (; li < lineCount; ++li)
            {
                g.DrawString(GetLineNumberString(1000-lineCount + li, maxDigitCount), lineNoFont, new SolidBrush(Theme.LineNumbersFont.Color), x1 + 1, y1 + li * lineHeight);
            }

            return newX2;
        }

        private void DrawCodeContent(Graphics g, int x1, int y1, int x2, int y2)
        {
            FontStyle fontStyle = FontStyle.Regular;
            if (Theme.CodeContentFont.Bold)
                fontStyle = FontStyle.Bold;
            if (Theme.CodeContentFont.Italic)
                fontStyle = fontStyle | FontStyle.Italic;

            Font codeFont = new Font(CodeConstants.GUI.ThemePreviewFontFace, Theme.MainContentFontSize, fontStyle, GraphicsUnit.Point);

            string sampleCode = CodeConstants.GUI.ThemePreviewSampleCodeLine;

            SizeF lineSize = MeasureStringWithSpaces(g, sampleCode, codeFont);
            int lineHeight = (int)lineSize.Height;
            int lineWidth = (int)lineSize.Width;

            FillBorder(g, Theme.CodeContentBackColor, x1, y1, x2, y2);

            int dotLineCount = CodeConstants.GUI.ThemePreviewDotLinesCount;
            int lineCount = (y2 - y1) / lineHeight;
            int linesAfter = (lineCount - dotLineCount) / 2;
            int linesBefore = lineCount - linesAfter - dotLineCount;

            int li = 0;
            for (li = 0; li < linesBefore; ++li)
            {
                g.DrawString(sampleCode, codeFont, new SolidBrush(Theme.CodeContentFont.Color), x1 + 1, y1 + li * lineHeight);
            }

            for (; li < linesBefore + dotLineCount; ++li)
            {
                g.DrawString(".", codeFont, new SolidBrush(Theme.CodeContentFont.Color), (x1 + 1) + (lineWidth / 2), y1 + li * lineHeight);
            }

            for (; li < lineCount; ++li)
            {
                g.DrawString(sampleCode, codeFont, new SolidBrush(Theme.CodeContentFont.Color), x1 + 1, y1 + li * lineHeight);
            }

        }

        private SizeF MeasureStringWithSpaces(Graphics g, string str, Font font)
        {
            return g.MeasureString(str, font,1000 , new StringFormat(StringFormatFlags.MeasureTrailingSpaces));
        }

        private string GetLineNumberString(int n, int maxDigitCount)
        {
            string sampleLineNo = n.ToString();

            int loopLen = maxDigitCount - sampleLineNo.Length;
            for (int i = 0; i < loopLen; ++i)
            {
                sampleLineNo = (Theme.LineNumbersShowZeros ? "0" : " ") + sampleLineNo;
            }

            if (Theme.LineNumbersShowStringAfter)
            {
                sampleLineNo += Theme.LineNumbersStringAfter;
            }

            for (int i = 0; i < Theme.LineNumbersSpacesBefore; ++i)
            {
                sampleLineNo = " " + sampleLineNo;
            }

            for (int i = 0; i < Theme.LineNumbersSpacesAfter; ++i)
            {
                sampleLineNo += " ";
            }

            return sampleLineNo;
        }

        private void ThemePreviewBox_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            g.FillRectangle(new SolidBrush(this.BackColor), 0, 0, this.Width, this.Height);

            int startx = this.InternalPadding;
            int endx = this.Width - this.InternalPadding;
            int cury = this.InternalPadding;
            int nexty = -1;
            FontStyle headerFontStyle = FontStyle.Regular;
            if (Theme.HeaderFont.Bold)
                headerFontStyle = FontStyle.Bold;
            if (Theme.HeaderFont.Italic)
                headerFontStyle = headerFontStyle | FontStyle.Italic;

            Font headerFont = new Font(CodeConstants.GUI.ThemePreviewFontFace, Theme.HeaderFontSize, headerFontStyle, GraphicsUnit.Point);

            nexty = cury + (int)g.MeasureString("M", headerFont).Height;

            FillBorder(g, Theme.HeaderBackColor, startx, cury, endx, nexty);
            DrawBorder(g, Theme.HeaderBorder, startx, cury, endx, nexty);
            g.DrawString(CodeConstants.GUI.ThemePreviewHeaderCaption, headerFont, new SolidBrush(Theme.HeaderFont.Color), new PointF((float)startx, (float)cury));

            cury = nexty + 1;
            nexty = this.Height - this.InternalPadding;
            int lineNoEndX = DrawLineNumbers(g, startx, cury, endx, nexty);
            DrawCodeContent(g, lineNoEndX + 1, cury, endx, nexty);

            DrawBorder(g, Theme.MainContentBorder, startx, cury, endx, nexty);

            FillBorder(g, this.BackColor, 0, 0, this.Width, this.InternalPadding -1 );
            FillBorder(g, this.BackColor, 0, 0, InternalPadding - 1, this.Height);
            FillBorder(g, this.BackColor, 0, this.Height - InternalPadding + 1, this.Width, this.Height);
            FillBorder(g, this.BackColor, this.Width - InternalPadding + 1, 0, this.Width, this.Height);
        }

        private void ThemePreviewBox_Resize(object sender, EventArgs e)
        {
            this.Invalidate();
        }
    }
}
